<?php

/**
 * Класс преобразует RQL-запросы в язык SQL
 * @author chedim
 * @category DAL
 * @package XDAL
 * @version 1
 *
 */
class RQLQuery {

	protected $source;
	protected  $sql;
	protected $checksum;
	protected static $cache = false;

	function __construct($rql) {
		$chk = $this->checksum = md5($rql);
		if (!self::$cache) self::$cache = new Cache('rqlcache');
			
		if (false == ($this->sql = self::$cache->$chk)) {
			$this->sql = $this->translate($rql);
			self::$cache->$chk = $this->sql;
		}
	}

	public function get_sql() {
		return $this->mk_sql($this->sql);
	}

	protected function translate($rql) {
		$arr =  $this->RQL2SQL($rql);
		return $arr['sql'];
	}

	protected function RQL2SQL($path, $wh = '', $op = 1) {
		if (!stristr($path, 'self')) $path = 'self, '.$path;
		$path = str_replace('self', 'user as self', $path);
		if ($wh) $wh.=' AND ';
		$wh .= 'self.id = '.(int)DataSource::$user->id;
		//preg_match_all("/([_a-z0-9]*)\s*\\[([^\\]]*)\\]/", $path, $params);
		// old line: $cbr = preg_replace("/([_a-z0-9]*)\s*\\[[^\\]]+\\]/", "\\1", $path);
		// new line: removing whitespaces
		$cbr = str_replace(" as ", '#_AS_#', $path);
		$cbr = str_replace(" ", '', $cbr);

		// parciing path
		// [1] - branch join operator present,
		// [2] - select node operator present,
		// [3] - node type,
		// [4] - next or branch start/end or path stop operators
		$rg = "/(>?)\\s*([$]?)([^><;.\\$,]+)\\s*([,;\\.]|<[?&])/";
		preg_match_all($rg, $cbr, $nodes);

			
		$arr = $this->RQL_CBR2ARR($nodes);
		$res = $this->RQL_ARR2SQL($arr, $op, $wh);
		$aq['from'] = implode(', ', $res['f']);

		$aq['where'] = '('.$res['w'].') AND ('.$wh.')';
		$aq['select'] = '255 as `###`, `'.@implode('`.*, 255 as `###`, `', $arr['selected']).'`.*';
		$aq['delete'] = '`'.@implode('`, `', $arr['sel']).'`';
		$result['sql'] = $aq;
		$result['types'] = $arr['selected'];

		return $result;
	}

	protected function RQL_CBR2ARR(&$cbrdata) {
		$res = array ();
		$res['elements'] = array ();
		$res['selected'] = array ();
		//$x = $start;
		while (!in_array($cbrdata[4][0], array(';','.')) && count($cbrdata[4])) {
			//				$rem = array_shift($cbrdata[1]);
			//				$sel = array_shift($cbrdata[2]);
			//				$pel = array_shift($cbrdata[3]);
			//				$rel = array_shift($cbrdata[4]);
			$join_branch_operator = array_shift($cbrdata[1]);
			$select_node_operator = array_shift($cbrdata[2]);
			$node_type_name = array_shift($cbrdata[3]);
			$next_node_relation = array_shift($cbrdata[4]);
			$current_element = false;
			if($select_node_operator) {
				$res['selected'][] = trim($node_type_name);
			}
			if (strstr($next_node_relation, '<')) {
				$current_element[] = (bool) strstr($next_node_relation, '&');
				while ($cbrdata[1][0] != '>') {
					$record = $this->RQL_CBR2ARR($cbrdata);
					$current_element[] = $record['elements'];
					$res['selected'] = array_merge($res['selected'], $record['selected']);
				}
			}

			if ($node_type_name != '--')$res['elements'][] = $node_type_name;
			if (@count($current_element) > 1) $res['elements'][] = $current_element;
		}
		if($cbrdata[3][0] != '--') $res['elements'][] = $cbrdata[3][0];
		$join_branch_operator = array_shift($cbrdata[1]);
		$select_node_operator = array_shift($cbrdata[2]);
		$node_type_name = array_shift($cbrdata[3]);
		$next_node_relation = array_shift($cbrdata[4]);
		if($select_node_operator && $node_type_name != '--') {
			$res['selected'][] = trim($node_type_name);
		}
		array_unshift($res['elements'], count($res['elements']));

		return $res;
	}

	protected function RQL_ARR2SQL($arr, $op = 1, $wh = '') {
		$res = array(
					'f' => array(),
					'w' => array()
		);

		$GLOBALS['RQL']['prev_relation_id'] = 0;
		static $sel = array();
			
		if (key_exists('elements', $arr)){
			$sel =  $arr['selected'];
			$arr = $arr['elements'];
		} else {

		}

		for ($x = 1; $x < count($arr)-1; $x++) {
			$parent = $arr[$x];
			$child = $arr[$x+1];

			if (!is_array($child) && !is_array($parent)) {
				// checking for «ass»
				$parent = $this->ascheck($parent);
				$child = $this->ascheck($child);
				$preq = (false !== array_search($parent[1], $sel));
				$preq = $preq || (false !== @strstr($wh, $parent[1]));
				$preq = $preq || ($parent[0] != $parent[1]);
				$creq = (false !== array_search($child[1], $sel));
				$creq = $creq || (false !== @strstr($wh, $child[1]));
				$creq = $creq || ($child[0] != $child[1]);
				$rel = $this->RQL_relation2SQL($parent, $child, $op, $preq, $creq);
				$res['f'] = array_merge($res['f'], $rel['f']);
				$res['w'][] = $rel['w'];
			} elseif (is_array($child) && !is_array($parent)) {
				$flag = $child[0];
				for ($y = 1;$y < count($child); $y++) {
					$child[$y][0] = $parent;
					array_unshift($child[$y], $flag);
					$child[$y][] = $arr[$x+2];
					$branch = array();
					$branch = $this->RQL_ARR2SQL($child[$y]);
					$res['f'] = array_merge($res['f'], $branch['f']);
					$branches['w'][] = $branch['w'];
				}
				if ($flag) {
					$res['w'][] = "((".implode(") AND (", $branches['w'])."))";
				} else {
					$res['w'][] = "((".implode(") OR (", $branches['w'])."))";
				}
			} elseif (is_array($parent)) {
				continue;
			} else {
				throw new Exception('Can\'t make relation between two branches arrays');
			}
		}
		$res['f'] = array_unique($res['f']);
		//$res['f'] = implode(', ', $res['f']);
		$res['w'] = implode("\r\nAND\r\n", $res['w']);
		$res['sel'] = $sel;
		return $res;
	}

	protected function RQL_relation2SQL($parent, $child, $racl = 1, $preq = false, $creq = false) {
		static $rid;


		if (!isset($parent[1])) $parent[1] = $parent[0];
		if (!isset($child[1])) $child[1] = $child[0];
		$prid = intval($rid)-1;

		if ($preq) {
			$res['f'][] = "`{$parent[0]}` as `{$parent[1]}`";
			$res['w'][] = "`r_$rid`.`parent` = '$parent[0]' AND `r_$rid`.`parent_id` = `$parent[1]`.`id` ";
		}

		if ($creq) {
			$res['f'][] = "`{$child[0]}` as `{$child[1]}`";
			$res['w'][] .= " `r_$rid`.`child_id` = `$child[1]`.`id` ";
		}
		$res['f'][] = "`relations` as `r_$rid`";
		$res['w'][] = (($prid > 0) ? "`r_$rid`.`parent` = '$parent[0]' AND `r_$rid`.`parent_id` = `r_$prid`.`child_id` " : 'true');
		$res['w'][] =	"`r_$rid`.`acl` & $racl = $racl ";
		$res['w'][] =	"`r_$rid`.`child` = '$child[0]' ";
			


		$res['w'] = ''.implode(' AND ', $res['w']).'';

		$res['id'] = $rid;


		$rid = intval($rid) + 1;
		return $res;
	}

	protected function mk_sql($aq) {
		//var_dump($aq);
		$query = 'SELECT DISTINCT '.
		((@$aq['select'])?$aq['select']:'*').
					' FROM '.$aq['from'].
		((@$aq['where'])?' WHERE '.$aq['where']:'').
		((@$aq['group'])?' GROUP BY '.$aq['where']:'').
		((@$aq['order'])?' ORDER BY '.$aq['order']:'').
		((@$aq['limit'])?' LIMIT '.$aq['limit']:'');
		return $query;
	}

	protected function ascheck($str) {
		$res = explode('#_AS_#',  $str);
		for ($x = 0; $x<2; $x++) {
			if (!@$res[$x]) @$res[$x] = $res[0];
			@$res[$x] = trim(@$res[$x]);
		}

		return $res;
	}


	public function exec() {
		$argc = func_num_args();
			
	}

}
?>